using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using AjaxStack.Support;
using com.yahoo.platform.yui.compressor;

namespace AjaxStack.Handlers
{
	/// <summary>
	/// Summary description for $codebehindclassname$
	/// </summary>
	public class ReleaseHandler : IHttpHandler
	{
		private static readonly string AppName = ConfigUtils.GetAppSetting("AppName", "app");
		private static readonly string ApiVersion = ConfigUtils.GetAppSetting("ApiVersion", "1.0");
		private static readonly List<string> ExcludeLinesWith = ConfigUtils.GetListFromAppSetting("ExcludeLinesWith");

		public void ProcessRequest(HttpContext context)
		{
			context.Response.ContentType = "text/javascript";

			var debug = context.Request.RawUrl.Contains("debug");
			var compress = context.Request["compress"] != null ? true : !debug;

			var specificVersion = context.Request["v"];
			if (!string.IsNullOrEmpty(specificVersion))
			{
				var fileName = string.Format("~/release/{0}-{1}.min.js", AppName, specificVersion);

				var filePath = fileName.MapHostAbsolutePath();

				if (File.Exists(filePath))
				{
					context.Response.WriteFile(filePath);
					return;
				}
			}

			GenerateReleaseVersion();

			var excludeLineFilter = debug ? null : ExcludeLinesWith;
			var appJs = JavascriptHandler.GetJavascriptAppTextExcluding(excludeLineFilter);
			var jsAppText = JavascriptHandler.WrapJavascriptInNamespace(appJs);
			var compressedAppJsInNamespace = CompressJavascript(jsAppText);
			var useJsText = compress ? compressedAppJsInNamespace : jsAppText;

			var appCss = CssHandler.GetAppCss();
			var compressedAppCss = CompressCss(appCss);
			var embedCssInJs = EmbedCssInJavascript(compressedAppCss);

			var allJsText = new StringBuilder();

			allJsText.AppendLine(GetJsLibJavascript(debug));
			allJsText.AppendLine(useJsText);
			allJsText.AppendLine(embedCssInJs);

			context.Response.Write(allJsText);

			//Used for intelli-sense in js/*.js files
			WriteAppJs(appJs);

			context.Response.End();
		}

		/// <summary>
		/// Gets the lib javascript.
		/// JSLibs ending with '.min.js' are already minified so don't re-compress
		/// </summary>
		/// <returns></returns>
		public static string GetLibJavascript(string relativeJsLibPath)
		{
			return GetLibJavascript(relativeJsLibPath, true);
		}

		public static string GetLibJavascript(string relativeJsLibPath, bool compress)
		{
			var jsLibPath = relativeJsLibPath.MapHostAbsolutePath();
			if (!Directory.Exists(jsLibPath)) return null;

			var sb = new StringBuilder();
			foreach (var jsFilePath in Directory.GetFiles(jsLibPath, "*.js", SearchOption.AllDirectories))
			{
				if (jsFilePath.InHiddenDirectory()) continue;

				//TODO: optimize
				var jsFileText = File.ReadAllText(jsFilePath);
				if (compress && !jsFileText.EndsWith(".min.js"))
				{
					jsFileText = CompressJavascript(jsFileText);
				}
				sb.AppendLine(jsFileText);
			}
			return sb.ToString();
		}

		public static string GetJsLibJavascript(bool debug)
		{
			const string debugPath = "~/jsLib.Debug/";
			if (debug && Directory.Exists(debugPath.MapHostAbsolutePath()))
			{
				return GetLibJavascript(debugPath, false);
			}
			return GetLibJavascript("~/jsLib/");
		}

		public static string GetJsLibOnloadJavascript()
		{
			return GetLibJavascript("~/jsLibOnLoad/");
		}

		public static void GenerateReleaseVersion()
		{
			//Embed the HTML files into: '~/js/App/AppPages.js'
			HtmlHandler.WriteAllPages();
			WriteAllFiles();
		}

		public static void WriteAllFiles()
		{
			var appJs = JavascriptHandler.GetJavascriptAppText();
			var compressedAppJs = CompressJavascript(appJs);
			var compressedAppJsInNamespace = JavascriptHandler.WrapJavascriptInNamespace(compressedAppJs);
			var appLibJs = GetJsLibJavascript(false);
			var appCss = CssHandler.GetAppCss();
			var compressedAppCss = CompressCss(appCss);
			var embedCssInJs = EmbedCssInJavascript(compressedAppCss);

			//WriteHelperFiles(appJs, appCss, compressedAppJsInNamespace);
			WriteAppJs(appJs);

			//This files is actually the only thing needed to run the app
			var jsApiFilename = string.Format("~/release/{0}-{1}.min.js", AppName, ApiVersion);
			var sb = new StringBuilder();
			sb.Append(appLibJs);
			sb.AppendLine(compressedAppJsInNamespace);
			sb.AppendLine(embedCssInJs);
			File.WriteAllText(jsApiFilename.MapHostAbsolutePath(), sb.ToString());

			var jsLibOnloadFilename = string.Format("~/release/{0}-{1}.onload.min.js", AppName, ApiVersion);
			File.WriteAllText(jsLibOnloadFilename.MapHostAbsolutePath(), GetJsLibOnloadJavascript());
		}

		private static void WriteHelperFiles(string appJs, string appCss, string compressedAppJsInNamespace)
		{
			WriteAppJs(appJs);

			var jsAppMinFilename = string.Format("~/release/jsApp.min.js");
			File.WriteAllText(jsAppMinFilename.MapHostAbsolutePath(),
			                  compressedAppJsInNamespace);

			var jsLibFilename = string.Format("~/release/jsLib.js");
			File.WriteAllText(jsLibFilename.MapHostAbsolutePath(), appJs);

			var cssApiFilename = string.Format("~/release/cssApp.css");
			File.WriteAllText(cssApiFilename.MapHostAbsolutePath(), appCss);
		}

		private static void WriteAppJs(string appJs)
		{
			var jsAppFilename = string.Format("~/release/jsApi-vsdoc.js");
			File.WriteAllText(jsAppFilename.MapHostAbsolutePath(), appJs);
		}

		public static string EmbedCssInJavascript(string cssText)
		{
			if (string.IsNullOrEmpty(cssText)) return null;
			var sb = new StringBuilder();
			sb.AppendFormat("document.write(\"\\r\\n<style type='text/css'>\\r\\n");
			sb.Append(cssText);
			sb.Append("\\r\\n</style>\\r\\n\");");
			return sb.ToString();
		}

		public static string CompressCss(string cssAppText)
		{
			if (string.IsNullOrEmpty(cssAppText)) return null;

			var javaReader = new java.io.StringReader(cssAppText);
			var javaWriter = new java.io.StringWriter();
			var cssCompressor = new CssCompressor(javaReader);

			const int noLineBreaks = -1;
			cssCompressor.compress(javaWriter, noLineBreaks);

			return javaWriter.toString();
		}

		public static string CompressJavascript(string jsAppText)
		{
			var javaReader = new java.io.StringReader(jsAppText);
			var javaWriter = new java.io.StringWriter();
			var jsCompressor = new JavaScriptCompressor(javaReader, new CSharpErrorReporter());

			const int noLineBreaks = -1;
			const bool munge = true;
			const bool verbose = false;
			const bool preserveAllSemiColons = false;
			const bool disableOptimisations = false;

			jsCompressor.compress(javaWriter,
			                      noLineBreaks,
			                      munge,
			                      verbose,
			                      preserveAllSemiColons,
			                      disableOptimisations);

			return javaWriter.toString();
		}

		public bool IsReusable
		{
			get
			{
				return false;
			}
		}
	}
}